#include <WinSock2.h>
#include "Writer.h"
#include "allocator.h"
#include "options.h"

Writer::Writer()
{
	msg_cnt = 0;
	snd_list.clear();
	wait_list.clear();
	sock = INVALID_SOCKET;
	snd_olap = 0;
	snd_stop = false; 
}

Writer::~Writer()
{
	while(snd_list.count>0)
	{
		msg* out_msg = snd_list.get();
		theAllocater->free_msg(out_msg);
	}

	while(wait_list.count>0)
	{
		msg* out_msg = wait_list.get();
		theAllocater->free_msg(out_msg);
	}
}

void Writer::begin_write(msg* outMsg)
{
	assert(outMsg->next==0);
	assert(outMsg->len()<=g_options.max_message_size);
	assert(outMsg->readpos>=0);
	assert(outMsg->readpos<g_options.max_message_size);

	lock.Lock();
	++msg_cnt;
	if(msg_cnt==1)
	{
		snd_list.add_tail(outMsg);
		lock.UnLock();
		write_impl();
	}else
	{
		wait_list.add_tail(outMsg);
		lock.UnLock();
	}	
}

#define max_snd_buff_count 512
//__declspec(thread) static WSABUF tls_sndbuf[max_snd_buff_count];

void Writer::end_write(bool success,uint32 transfered,bool cont)
{
	int count = snd_list.count;
	while(snd_list.count>0)
	{
		msg* out_msg = snd_list.get();
		transfered -= out_msg->len();
		theAllocater->free_msg(out_msg);
	}
	
	if ( success )
		assert(transfered==0);

	lock.Lock();
	assert(msg_cnt>=count);	
	msg_cnt -=count;

	bool hit = false;
	if(!cont||!success)
	{
		snd_stop = true;
	}
	else
	{
		uint32 snd_size = 0;
		while((wait_list.count>0)&&(snd_list.count<max_snd_buff_count))
		{
			msg* out_msg = wait_list.head;
			uint32 v = snd_size + out_msg->len();

			if(v>g_options.max_message_size) break;

			wait_list.get();
			snd_size = v;
			snd_list.add_tail(out_msg);
			hit = true;
		}
		assert(snd_list.count<=msg_cnt);
		assert(snd_list.count<=max_snd_buff_count);
	}
	lock.UnLock();

	if(hit)
		write_impl();
}

void
Writer::write_impl()
{
	int count = snd_list.count;
	assert(count<=max_snd_buff_count);
	msg* out_msg = snd_list.head;;
	for(int i=0;i<count;++i)
	{
		assert(out_msg!=0);
		tls_sndbuf[i].buf = out_msg->base();
		tls_sndbuf[i].len = out_msg->len();
		out_msg = out_msg->next;
	}

	assert(snd_list.count==count);
	snd_olap->reset(0);
	DWORD transfered = 0;
	if(::WSASend(sock,tls_sndbuf,(DWORD)count,&transfered,0,snd_olap,0)==SOCKET_ERROR)
	{
		if(WSAGetLastError()!=WSA_IO_PENDING)
			end_write(false,0,false);
	}
}